﻿import copy
from game_elements import *
from random import *
from tdodo import *

class Solution:
    D = 4 #解長度
    parameters = []
    game_map = Map(100, 100, 1)
    towers = []
    path = [(0, 0), (0, 0)]
    def __init__(self, numbers = None):
        self.score = -1
        self.evaluated = False
        self.numbers = Solution._init_numbers() if numbers is None else numbers

    @classmethod
    def _init_numbers(cls):
        t = [0] * 4
        t[0] = uniform(0, 50)
        t[1] = uniform(0, 100)
        t[2] = uniform(0, 4)
        t[3] = uniform(0, 100)
        return t

    def get_score(self):
        if self.evaluated == False:
            self.score = 0
            monster_amount = int(Solution.parameters[0] + self.numbers[0])
            max_hp = Solution.parameters[1] + self.numbers[1]
            speed = Solution.parameters[2] + self.numbers[2]
            armor = Solution.parameters[3] + self.numbers[3]
            g = {'pos': Solution.path[1], 'id': 1}
            special_monser = Monster(max_hp, speed, armor, Solution.path[0], goal=g)
            monsters = [copy.deepcopy(special_monser) for i in range(monster_amount)]
            log = {'all_d':0, 'max_d':0}
            evaluate(Solution.towers, monsters, Solution.game_map, None, log)
            max_d = log['max_d']
            self.score += max_d * 10
            money_used = Solution.money_spend(monster_amount, max_hp, speed, armor)
            self.score -= money_used
            #test
            #for x in self.numbers:
                #self.score += x ** 2
            #self.score *= -1
        return self.score

    def reset_score(self):
        self.evaluated = False

    @classmethod
    def money_spend(cls, amount, hp , speed, armor):
        answer = (amount - cls.parameters[0]) * 20
        answer += (hp - cls.parameters[1]) * 5
        answer += (speed - cls.parameters[2]) * 250
        answer += (armor - cls.parameters[3]) * 5

        return answer

    def __getitem__(self, index):
        return self.numbers[index]

    def __setitem__(self, index, value):
        self.numbers[index] = value

    def __sub__(self, other):
        a = [self.numbers[i] - other.numbers[i] for i in xrange(Solution.D)]
        return Solution(a)

    def __mul__(self, scalar):
        a = [self.numbers[i] * scalar for i in xrange(Solution.D)]
        return Solution(a)

class DE:
    POPULAION_SIZE = 12
    def __init__(self, game_map, towers, parameters, path):
        self.game_map = game_map
        self.towers = towers
        self.parameters = parameters
        Solution.parameters = parameters
        Solution.game_map = game_map
        Solution.towers = towers
        Solution.path = path

    def get_solution(self, run_time, F, CR):
        max_now = -99999999
        first_score = 0
        best_solution = Solution()
        targets = [Solution() for i in xrange(DE.POPULAION_SIZE)]
        trials = [Solution() for i in xrange(DE.POPULAION_SIZE)]

        for _run in xrange(run_time):
            for t_ctr in xrange(DE.POPULAION_SIZE):
                trials[t_ctr].reset_score()
                random_indices = self.get_random_three_indices(t_ctr)
                j = randint(0, Solution.D - 1)
                diff = targets[random_indices[1]] - targets[random_indices[2]]
                for i in xrange(Solution.D):
                    if random() < CR or i == j:
                        trials[t_ctr][i] = targets[random_indices[0]][i] + diff[i] * F                        
                        if trials[t_ctr][i] > 100 :pass
                            #trials[t_ctr][i] = 100
                        elif trials[t_ctr][i] < Solution.parameters[i] :
                            trials[t_ctr][i] = Solution.parameters[i]
                    else:
                        trials[t_ctr][i] = targets[t_ctr][i]

            for t_ctr in xrange(DE.POPULAION_SIZE):
                if trials[t_ctr].get_score() > targets[t_ctr].get_score():
                    for i in xrange(Solution.D):
                        targets[t_ctr][i] = trials[t_ctr][i]
                if targets[t_ctr].get_score() > max_now:
                    max_now = targets[t_ctr].get_score()
                    best_solution = targets[t_ctr]
            
            if(first_score == 0): first_score = max_now
        print max_now
        print "improvement:", (max_now - first_score)
        """
        nums = best_solution.numbers
        ps = Solution.parameters
        print "amount:" ,nums[0] + ps[0],  "  HP: ", nums[1] + ps[1], " speed: ", nums[2]+ps[2], " armor: ", nums[3]+ps[3]
        """
        return best_solution

    def get_random_three_indices(self, e):
        P = DE.POPULAION_SIZE - 1
        def random_num_except(xs):
            t = randint(0, P)
            return t if not (t in xs) else random_num_except(xs)

        answer = []
        for i in range(3):
            answer.append(random_num_except(answer + [e]))

        return answer


#if __name__ == '__main__':
   # game_map = Map(100, 100, 1)
   # towers = []
   # list1 = []
   # de = DE(game_map, towers, list1)
   # Solution.DE = de
  #  de.get_solution(300, 0.5, 0.5)
